package net.sk.china.city.talk.service.impl;

import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import net.sk.china.city.talk.constants.Constants;
import net.sk.china.city.talk.service.CallbackService;
import net.sk.china.city.talk.service.system.SystemConfigService;
import net.sk.china.common.exception.TalkException;
import net.sk.china.common.response.WechatRefundNotifyResponse;
import net.sk.china.common.utils.RedisUtil;
import net.sk.china.common.utils.TalkUtil;
import net.sk.china.common.utils.WxPayUtil;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.Security;
import java.util.Base64;
import java.util.Map;

/**
 * CallbackServiceImpl 接口实现
 * +----------------------------------------------------------------------
 * | Talk To [ 聊来改善地区金融服务环境，助力企业发展 ]
 * +----------------------------------------------------------------------
 * | Copyright (c) 2018-2024 聊来All rights reserved.
 * +----------------------------------------------------------------------
 * | Licensed ( <a href="http://www.apache.org/licenses/LICENSE-2.0">apache</a> )
 * +----------------------------------------------------------------------
 * | @Author: 聊来 <18970881148@qq.com>
 * +----------------------------------------------------------------------
 * | DateTime: 2024/6/2 22:33
 * +----------------------------------------------------------------------
 */
@Service
public class CallbackServiceImpl implements CallbackService {

    private static final Logger logger = LoggerFactory.getLogger(CallbackServiceImpl.class);

    private final RedisUtil redisUtil;

    private final SystemConfigService systemConfigService;

    public CallbackServiceImpl(RedisUtil redisUtil, SystemConfigService systemConfigService) {
        this.redisUtil = redisUtil;
        this.systemConfigService = systemConfigService;
    }

    /**
     * 微信退款回调
     * @param xmlInfo 微信回调json
     * @return {@code String}
     */
    @Override
    public String weChatRefund(String xmlInfo) {
        logger.info("微信退款异步通知：" + xmlInfo);

        StringBuilder sb = new StringBuilder();
        sb.append("<xml>");
        if(StrUtil.isBlank(xmlInfo)){
            sb.append("<return_code><![CDATA[FAIL]]></return_code>");
            sb.append("<return_msg><![CDATA[xmlInfo is blank]]></return_msg>");
            sb.append("</xml>");
            logger.error("wechat refund callback error : " + sb.toString());
            return sb.toString();
        }

        Map<String, String> respMap;
        try {
            respMap = WxPayUtil.xmlToMap(xmlInfo);
        } catch (Exception e) {
            sb.append("<return_code><![CDATA[FAIL]]></return_code>");
            sb.append("<return_msg><![CDATA[").append(e.getMessage()).append("]]></return_msg>");
            sb.append("</xml>");
            logger.error("wechat refund callback error : " + e.getMessage());
            return sb.toString();
        }
        // 这里的可以应该根据小程序还是公众号区分
        String returnCode = respMap.get("return_code");
        if (returnCode.equals(net.sk.china.common.constants.Constants.SUCCESS)) {
            String appId = respMap.get("appid");
            String signKey = getSignKey(appId);
            // 解码加密信息
            String reqInfo = respMap.get("req_info");
            try {
                String decodeInfo = decryptToStr(reqInfo, signKey);
                Map<String, String> map = WxPayUtil.xmlToMap(decodeInfo);

                WechatRefundNotifyResponse response = TalkUtil.mapToObj(map, WechatRefundNotifyResponse.class);
                logger.info("微信退款订单已确认成功==> 根据相应模块写具体相关的逻辑代码" + response);

                sb.append("<return_code><![CDATA[SUCCESS]]></return_code>");
                sb.append("<return_msg><![CDATA[OK]]></return_msg>");
            } catch (Exception e) {
                sb.append("<return_code><![CDATA[FAIL]]></return_code>");
                sb.append("<return_msg><![CDATA[").append(e.getMessage()).append("]]></return_msg>");
                logger.error("微信退款回调异常：" + e.getMessage());
            }
        } else {
            sb.append("<return_code><![CDATA[FAIL]]></return_code>");
            sb.append("<return_msg><![CDATA[").append(respMap.get("return_msg")).append("]]></return_msg>");
            logger.error("wechat pay error : 业务异常==》" + respMap.get("return_msg"));
        }
        sb.append("</xml>");
        return sb.toString();
    }

    /**
     * 获取微信公众号、小程序、开发者key
     * @param appId appId
     * @return String
     */
    private String getSignKey(String appId) {
        String publicAppid = systemConfigService.getValueByKey(Constants.CONFIG_KEY_PAY_WE_CHAT_APP_ID);
        String miniAppid = systemConfigService.getValueByKey(Constants.CONFIG_KEY_PAY_ROUTINE_APP_ID);
        String appAppid = systemConfigService.getValueByKey(Constants.CONFIG_KEY_PAY_WX_APP_ID);
        String signKey = "";
        if (StrUtil.isBlank(publicAppid) && StrUtil.isBlank(miniAppid) && StrUtil.isBlank(appAppid)) {
            throw new TalkException("pay_weixin_appid或pay_routine_appid、pay_wxapp_appid不能都为空");
        }
        if (StrUtil.isNotBlank(publicAppid) && appId.equals(publicAppid)) {
            signKey = systemConfigService.getValueByKeyException(Constants.CONFIG_KEY_PAY_WE_CHAT_APP_KEY);
        }
        if (StrUtil.isNotBlank(miniAppid) && appId.equals(miniAppid)) {
            signKey = systemConfigService.getValueByKeyException(Constants.CONFIG_KEY_PAY_ROUTINE_APP_KEY);
        }
        if (StrUtil.isNotBlank(appAppid) && appId.equals(appAppid)) {
            signKey = systemConfigService.getValueByKeyException(Constants.CONFIG_KEY_PAY_WX_APP_ID);
        }
        return signKey;
    }

    /**
     * java自带的是PKCS5Padding填充，不支持PKCS7Padding填充。
     * 通过BouncyCastle组件来让java里面支持PKCS7Padding填充
     * 在加解密之前加上：Security.addProvider(new BouncyCastleProvider())，
     * 并给Cipher.getInstance方法传入参数来指定Java使用这个库里的加/解密算法。
     */
    private static String decryptToStr(String reqInfo, String signKey) throws Exception {
        Security.addProvider(new BouncyCastleProvider());
        byte[] decodeReqInfo = base64DecodeJustForWxPay(reqInfo).getBytes(StandardCharsets.ISO_8859_1);
        SecretKeySpec key = new SecretKeySpec(SecureUtil.md5(signKey).toLowerCase().getBytes(StandardCharsets.UTF_8), "AES");
        Cipher cipher;
        cipher = Cipher.getInstance("AES/ECB/PKCS7Padding");
        cipher.init(Cipher.DECRYPT_MODE, key);
        return new String(cipher.doFinal(decodeReqInfo), StandardCharsets.UTF_8);
    }

    /**
     * 仅仅为微信解析密文使用
     * @param source 待解析密文
     * @return 结果
     */
    private static String base64DecodeJustForWxPay(final String source) {
        Base64.Decoder decoder = Base64.getDecoder();
        return new String(decoder.decode(source), StandardCharsets.ISO_8859_1);
    }
}
